/*!
 Temelia - List common interface.

 Copyright (C) 2008 Ceata (http://ceata.org/proiecte/temelia).

 @author Dascalu Laurentiu, Macovei Alexandru

 This program is free software; you can redistribute it and
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 3
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 */

/*
 *	This file is used to define macros used in linked_list and
 *	doubly_linked_list to avoid duplicating code.
 */

/*
 * Confucius says:
 *   He who wants to properly debug this mess must either copy the code over in
 * the source file to set breakpoints there, then come back here and redo the
 * macro, or, set breakpoints in the preprocessed code, then also come back
 * here and redo the macro.
 *
 * And Confucius may be wrong here.
 */

#ifndef LISTCOMMON_H_
#define LISTCOMMON_H_

#include "common.h"
#include "iterator.h"

/*!
 * @param Type either linked_list or doubly_linked_list
 */
#define LIST_NEW(type, L)\
	do\
	{\
		L = (struct _##type *) _new(sizeof(struct _##type));\
		_ASSERT(L, ==, NULL, NULL_POINTER, NULL);\
		L->size = 0;\
		L->begin = NULL;\
		L->end = NULL;\
	}while(0)\

#define LIST_CLONE(type, list, it, clone_list, clone, context)\
do\
{\
	clone_list = type ## _new();\
\
	if (clone != NULL)\
	{\
		for (it = type ## _get_begin(list); it != NULL;\
				it = type ## _iterator_get_next(it))\
			type ## _push_back(clone_list,\
					clone(type ## _iterator_get_key(it), context));\
	}\
	else\
	{\
		for (it = type ## _get_begin(list); it != NULL;\
				it 	= type ## _iterator_get_next(it))\
			type ## _push_back(clone_list,\
					type ## _iterator_get_key(it));\
	}\
} while(0)\

/*!
 * @param Type either linked_list or doubly_linked_list
 */
#define LIST_DELETE(L, crt, prc, type)\
	do\
	{\
		_ASSERT(L, ==, NULL, NULL_POINTER,);\
		crt = L->begin;\
		while (crt != NULL)\
		{\
			prc = crt;\
			crt = type##_get_next(crt);\
			type##_delete(prc);\
		}\
		_delete(L);\
	}while(0)\

/*!
 * @param Type either linked_list or doubly_linked_list
 */
#define LIST_SORT(L, it, aux, compare, context, type)\
	do\
	{\
		int ordonat;\
		_ASSERT(L, ==, NULL, NULL_POINTER,);\
		_ASSERT(compare, ==, NULL, NULL_POINTER,);\
		do\
		{\
			ordonat = 0;\
			for (it = L->begin; type##_get_next(it) != NULL; it = type##_get_next(it))\
			{\
				if (compare(type##_get_key(it), type##_get_key(type##_get_next(it)), context) > 0)\
				{\
					aux = type##_get_key(it);\
					type##_set_key(it, type##_get_key(type##_get_next(it)));\
					type##_set_key(type##_get_next(it), aux);\
					ordonat = 1;\
				}\
			}\
		} while (ordonat);\
	} while(0)\

#define LIST_MERGE(L3, it1, it2, key1, key2, type, compare, context)\
	do\
	{\
		for (; it1 && it2;)\
		{\
			key1 = type##_iterator_get_key(it1);\
			key2 = type##_iterator_get_key(it2);\
\
			if (compare(key1, key2, context) < 0)\
			{\
				type##_push_back(L3, key1);\
				it1 = type##_iterator_get_next(it1);\
			}\
			else\
			{\
				type##_push_back(L3, key2);\
				it2 = type##_iterator_get_next(it2);\
			}\
		}\
\
	for (; it1; it1 = type##_iterator_get_next(it1))\
		type##_push_back(L3, type##_iterator_get_key(it1));\
\
	for (; it2; it2 = type##_iterator_get_next(it2))\
		type##_push_back(L3, type##_iterator_get_key(it2));\
\
	}while(0)\

#endif /* LISTCOMMON_H_ */
